/*

	objx OO JavaScript Library
	Copyright (c) 2008 Mat Ryer
	
	Please visit the project home at http://objx.googlecode.com/
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:
	
	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.
	
	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

*/

// define (or extend) the objx object
var objx = objx || { version: 1.2 };

// debug mode is true by default
objx.debug = true;

// gets the arguments from the specified function
// and returns the names in an array
objx.args = function(f){
    // from Prototype JavaScript library

    var args = f.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",");
	
	for (var i = 0, l = args.length; i < l; i++)
		args[i] = args[i].strip();
	
    return args.length == 1 && !args[0] ? [] : args;
};

// counts the number of arguments on a function
objx.argc = function(f){
    return objx.args(f).length;
};

// converts a string to pascal case
objx.pascal = function(s){
	if (!s) return s;
	if (s.length < 2) return s.toUpperCase();
	return s.substring(0,1).toUpperCase() + s.substring(1);
};

// get the querystring parameters as an object
objx.qs = function(){

	if (objx._qs) return objx._qs;

	// Build an empty URL structure in which we will store
	// the individual query values by key.
	objx._qs = {};
	
	window.location.search.replace(
		new RegExp( "([^?=&]+)(=([^&]*))?", "g" ),
		function( $0, $1, $2, $3 ){
			objx._qs[ $1 ] = $3;
		}
	);

	return objx._qs;

};

// define an empty function
objx.nf = function(){};

// st stamps an object with a specific marker
objx.st = function(k,o,v){
    o.objx = o.objx || {};
    o.objx[k] = v || true;
    return o;
};

// ex extends one object with another or multiple others
objx.ex = function(){
    for (var i = 1, l = arguments.length; i < l; i++)
        for (var property in arguments[i])
            arguments[0][property] = arguments[i][property];
	return arguments[0];
};

objx.ex$ = function(){
	var o = {};
	var args = [];
	
	args.push(o);
	
	for (var i = 0, l = arguments.length; i<l; i++)
		args.push(arguments[i]);
	
	return objx.ex.apply(objx, args);
}

// gets whether the specified object is a function or not
objx.isf = function(o){
    if (!o) return false;
    return typeof o == "function";
};

// gets whether the specified object is a boolean or not
objx.isb = function(o){
    return typeof o == "boolean";
};

// gets whether the specified object is an interface or not
objx.isi = function(o){
	if (!o) return false;
	if (o.objx && o.objx.t && o.objx.t == "i")
		return true;
	else
		return false;
};

objx.isc = function(o){
	if (!o) return false;
	if (o.objx && o.objx.t && o.objx.t == "c")
		return true;
	else
		return false;
};

objx.bind = function(){

	var _func = arguments[0] || null;
	var _obj = arguments[1] || this;
	var _args = [];
	
	// add arguments
	for (var i = 2, l = arguments.length; i<l; i++)
		_args.push(arguments[i]);

	// return a new function that wraps everything up
	return function() {
		
		// start an array to get the args
		var theArgs = [];
	
		// add every argument from _args
		for (var i = 0, l = _args.length; i < l; i++)
			theArgs.push(_args[i]);
		
		// add any real arguments passed
		for (var i = 0, l = arguments.length; i < l; i++)
			theArgs.push(arguments[i]);
	
		// call the function with the specified context and arguments
		return _func.apply(_obj, theArgs);

	};
	
};

objx.ex(String.prototype, {
	strip: function() {
		return this.replace(/^\s+/, '').replace(/\s+$/, '');
	}
});

// observes an event
objx.observe = function(obj, ev, cb){

	// make sure the object has an objx property
	obj.objx = obj.objx || {};
	
	// make sure the objx object has an 'ev' property
	obj.objx.ev = obj.objx.ev || {};
	
	// make sure the ev property has a property called
	// the event name, and that it is an array
	obj.objx.ev[ev] = obj.objx.ev[ev] || [];
	
	// add the callback
	obj.objx.ev[ev].push(cb);
	
};

// notifies an event
objx.notify = function(obj, ev){

	// check for invalid object type
	if (objx.debug && !obj) throw new objx.exceptions.ArgumentException("Can not call notify() on an object of type " + (typeof obj));
	
	// do we have any callbacks for this object and event name?
	if (!obj.objx || !obj.objx.ev || !obj.objx.ev[ev]) return false;
	
	// get the event callbacks
	var cbs = obj.objx.ev[ev];
	
	// create the array of arguments to pass to the callbacks
	var args = [obj];
	
	// add any additional arguments that were passed to this function
	if (arguments.length > 1)
		for (var i = 2, l = arguments.length; i < l; i++)
			args.push(arguments[i]);

	// loop through and call each callback
	for (var i = 0, l = cbs.length; i < l; i++)
		cbs[i].apply(null, args);
	
};

// the method that gets called when event shortcut
// methods are created.
objx.eventMethod = function(ev){

	if (objx.isf(arguments[1]))
		// add the observer
		return objx.observe.apply(this, objx.__colargs(this, arguments));
	else
		// notify the event
		return objx.notify.apply(this, objx.__colargs(this, arguments));

};

// define the objx class
objx.Class = function(){

    // by default, assume not a mixin
    var mixin = false;

    // check some initial stuff (if in debug mode)
    if (objx.debug){
        
        // ensure we have at least one argument
        if (arguments.length == 0) throw new objx.exceptions.ArgumentException("At least one argument expected when calling objx.Class().");
        
        if (!arguments[arguments.length - 1])
            throw new objx.exceptions.ArgumentException("The last argument passed to objx.Class() can not be null or undefined.");
            
        // ensure the definition object (last argument) is an object
        if (typeof arguments[arguments.length - 1] != "object")
            throw new objx.exceptions.ArgumentException("The last argument passed to objx.Class() must be an object defining the class.");
        
    }
    
    // get the class definition object (always the last thing passed)
    var cd = arguments[arguments.length - 1];

	var klass = null;
	if (objx.debug && cd.objx && cd.objx.ab) {
	
		// abstract class (non instantiatable)
		
		klass = function(){
            throw new objx.exceptions.ClassException("Can not instantiate an abstract class.  This class must be subclassed first.");
 		}
 		
 	}
	else
	{
	
		// instantiatable class
		klass = function(){
		
			// create a new instance of the objx object
			this.objx = {};
		
	        // call the constructor
	        if (!klass.supressInit)
	        	this.init.apply(this, arguments);
	        
	    }
	    
	}
	
    // keep track of interfaces implemented
    klass.interfaces = [];
    
    // and events
    klass.events = [];
   
    // iterate through any classes, interfaces or mixins that were
    // specified
    for (var i = 0, l = arguments.length - 1; i < l; i++)
    {
    
        if (arguments[i] && arguments[i].objx)
        {
            switch (arguments[i].objx.t)
            {
                case "c": // (parent) class
                
                    // check to make sure only one base class is defined
                    if (objx.debug && cd.base)
                        throw new objx.exceptions.ClassException("Only one class can be specified to inherit from.");
                    
                    // keep the base prototype for reference
                    cd.base = arguments[i].prototype;

                    // is this base class abstract? and does it have any 
                    // virtual methods assigned to the aci (Abstract Class Interface)
                    if (arguments[i].objx && arguments[i].objx.aci)
                    {
                        // ensure the class definition can act as this interface
                        if (!objx.aa(cd, arguments[i].objx.aci, true))
                            throw new objx.exceptions.DefinitionException("Base class abstract methods not satisfied.");
                    }
                    
                    // loop through each property in the base prototype
                    for (var property in cd.base)
                    {
                    
                        // is this a non system class function?
                        // System class functions (see objx.classMethods) are ones that we shouldn't
                        // bother base_ ing because we'll only be setting them back again later!
                        if (objx.isf(cd.base[property]) && (objx.classMethods[property] == undefined))
                        {
                            
                            // get the base function
                            var baseFunction = cd.base[property];

                            // if the new definition doesn't override this,
                            // use the base version.
                            if (!cd[property])
                                cd[property] = baseFunction;
                            else
                            	// add the base version of this method
                            	cd["base_" + property] = baseFunction;
                            
                        }
                        else if(!objx.isf(cd.base[property]))
                        {
                        	// copy across fields
                        	if (cd[property] == undefined)
                        		cd[property] = cd.base[property];
                        }
                        
                    }

                    break;
                case "i": // interface
                
                    // make sure everything in the interface is defined
                    // in the class definition
                    
                    if (objx.debug && !objx.aa(cd, arguments[i], true))
                        throw new objx.exceptions.DefinitionException("Interface is not properly satisfied.");
                    
                    // add this interface
                    klass.interfaces.push(arguments[i]);
                    
                    break;

                default:
                    mixin = true;
                    break;
            }
        }
        else
            mixin = true;
    
        
        if (mixin)
            // if what is specified is an object, just mix it in
            if (typeof arguments[i] == "object")
            {
                // mix-in this object
                objx.ex(cd, arguments[i]);
            }
        
    }
    
    // collect the interface definition (if abstract)
    var interfaceDef = null;
    
    // loop through each property in the class definition
    for (var property in cd)
    {
    
        // are we in debug mode and is this a non-abstract class
        if (objx.debug && (!cd.objx || !cd.objx.ab))
        {
            // throw an exception if they have used Method()'s here
            if (cd[property] && !objx.isf(cd[property]) && cd[property].objx && cd[property].objx.t == "m")
                throw new objx.exceptions.DefinitionException("Can not define a Method() placeholder in a non-abstract class.  Either remove the Method() definition or use AbstractClass({ ... }).");
        
        }
        
        // is this an abstract class?
        if (cd.objx && cd.objx.ab)
        {
            // is this a Method()?
            if (cd[property] && !objx.isf(cd[property]) && cd[property].objx && cd[property].objx.t == "m")
            {
                
                // ensure we have an interface definition
                if (!interfaceDef) interfaceDef = {};
                
                // add this Method() to the interface definition
                interfaceDef[property] = cd[property];
                
            }
        }
        
        // is this an event?
        if (cd[property] && cd[property].objx && cd[property].objx.t && cd[property].objx.t == "e")
       	{
       	
       		// save this event
       		klass.events.push(cd[property]);

       		// add the event method to the class definition
       		cd[property] = function(property){
       			return function(){
					return objx.eventMethod.apply(this, objx.__colargs(property, arguments));
				}
			}(property);

       		// create the notify direct function
       		cd["notify_" + property] = function(property){
       			return function(){
       				return this.notify.apply(this, objx.__colargs(property, arguments));
       			}
       		}(property);
       		
       		// create the observe direct function
       		cd["observe_" + property] = function(property){
       			return function(){
       				return this.observe.apply(this, objx.__colargs(property, arguments));
       			}
       		}(property);
       		
       	}
       	
       	// is this a property?
       	if (cd[property] && cd[property].objx && cd[property].objx.t && cd[property].objx.t == "p")
       	{
       	
       		// call the extender on the property
       		cd[property].extend(cd, property);
       		
       		// nullify the property
       		cd[property] = null;
       	
       	}

    }

    // do we have an interface definition?
    if (interfaceDef)
        // stamp the abstract class interface to the definition
        objx.st("aci", klass, objx.i(interfaceDef));

    // mix-in the for-free class and instance methods
    objx.ex(cd, objx.classMethods);
    objx.ex(klass, objx.classMethods);

    // if they have specified no constructor, set a default one
    if (!cd.init)
        cd.init = objx.nf;
    
    // add the class definitions
    objx.ex(klass.prototype, cd);
    
    // do toString explicitly for IE
    klass.prototype.toString = cd.toString;
    
    // set the type
    klass.prototype.type = klass;

    // set the constructor
    klass.prototype.constructor = klass;
    
    // mark the type 't' as class 'c'
    objx.st("t", klass, "c");
    
    // return the klass
    return klass;
    
};

/*
 *  Internal method for turning arguments into a real
 *  array with optional context at the start
 */
objx.__colargs = function()
{

    var args;
    var aaIndex = 0;
    
    if (arguments.length == 2)
    {
        args = [arguments[0]];
        aaIndex++;
    }
    
    for (var i = 0, l = arguments[aaIndex].length; i < l; i++)
        args.push(arguments[aaIndex][i]);
        
    return args;
    
};

/*
 *  classMethods get added to all Classes defined by objx
 *  and instantiated objects.
 */
objx.classMethods = {

    // shortcut to actsAs
	actsAs: function(){
	    return objx.aa.apply(this, objx.__colargs(this, arguments));
	},
	
	// shortcut to implements
	implements: function(){
		return objx.im.apply(this, objx.__colargs(this, arguments));
	},
	
	// shortcut to observe
	observe: function(ev,cb){
		objx.observe(this, ev, cb);
		return this;
	},
	
	// shortcut to notify
	notify: function(ev){
		objx.notify.apply(this, objx.__colargs(this, arguments));
		return this;
	}
	
};

// the class for an interface
objx.Interface = objx.Class({

    def: null,
    
    init: function(def){
        
        if (objx.debug && !def)
            throw new objx.exceptions.ArgumentException("An interface definition must be passed when creating a new interface.  This should be an object of methods or a series of string arguments.");
        
        // save the args
        this.def = def;
        
        // mark this item as an interface
        objx.st("t", this, "i");

    },
    
    toString: function(){
        return "[Object: Interface]"; 
    }
    
});

objx.json = function(o){

    // TODO: make this better

    var o = o || "null";
    
    if (typeof o == "number")
    	return o.toString();
    
    if (typeof o == "string")
        return "\"" + o + "\"";
    
    if (typeof o == "function" || typeof o == "boolean")
        return o.toString();
    
    var j = "{";
    
    for (var property in o)
        j += "\t" + property + ":" + objx.json(o[property]) + "\n";
    
    j += "}";
    
    return j;

};

// a utility method for creating interfaces
objx.interface = function(){

    if (arguments.length > 1 || typeof arguments[0] == "string")
        return objx.ii.apply(this, arguments);

    return new objx.Interface(arguments[0]);
    
};

// inline interface definition shortcut
objx.ii = function(){
    
    var definition = {};
    
    for (var i = 0, l = arguments.length; i < l; i++)
        definition[arguments[i]] = objx.m();
    
    return objx.i(definition);
    
};

// interface classes

// method class - used to describe a method
objx.Method = objx.Class({

    argc: null,
    init: function(args){

        // save the arguments
        this.args = args;
        
        // mark this item as a method description
        objx.st("t", this, "m");
            
        if (!args || !args.length) return;

        if (this.args.length == 1 && typeof this.args[0] == "number")
            // number of arguments specified
            this.argc = this.args[0];
        if (typeof this.args[0] == "string")
            // just the argument names
            this.signature = args;
        
    }
    
});

// shortcut function method 
objx.method = function(){
    return new objx.Method(arguments);
};

// event object
objx.Event = objx.Class({
	init: function(args){
		
		// save the args
		this.args = args;
		
		// mark this item as an event description
        objx.st("t", this, "e");
        
	}
});

objx.event = function(){
	return new objx.Event(arguments);
};

// stamper methods

// mark a class as abstract
objx.abstract = function(o){
    return objx.st("ab", o);
};

/*
 * the implements method - to check if an interface explicitly implements an interface
 * or collection of interfaces
 *
 * objx.implements(testObject, [interface1], [interface2])
 * 
 *      testObject              -   the object to test
 *      interface1              -   an interface to check for
 *      interface2              -   another interface to check for
 *
*/
objx.implements = function(){

    var interfaces = objx.isf(arguments[0]) ? arguments[0].interfaces : arguments[0].type.interfaces;
    //var throwOnFailure = false;
    var argsLen = arguments.length;
    
    // loop through the interfaces
    for (var i = 1; i < argsLen; i++)
    {

        // by default, we haven't found this
        var found = false;
        
        // check for this interface in the objects interfaces
        for (var ii = 0, ll = interfaces.length; ii < ll; ii++)
        {
        
            // was this interface found?
            if (interfaces[ii] == arguments[i])
            {
                // yes - found it
                found = true;
                
                // stop checking
                break;
            }
        }
        
        // this interface was not implemented by this class definition
        if (!found)
            return false;
        
    }
    
    // everything passed OK
    return true;

};

// tests to see if the test object satisfies the interfaces passed or not.
objx.actsAs = function(){

    // get the object to test
    var test = objx.isf(arguments[0]) ? arguments[0].prototype : arguments[0];
    
    var throwOnFailure = false;
    var argsLen = arguments.length;
    
    // if the last argument is a boolean - then take this to be
    // whether or not we should throw an exception on failures
    if (objx.isb(arguments[arguments.length - 1]))
    {
    	// set the value
        throwOnFailure = arguments[arguments.length-1];
        // and ignore this argument in the future
        argsLen--;
    }
    
    for (var i = 1, l = argsLen; i < l; i++)
    {
        var iface = arguments[i];
        
        if (objx.debug && iface.objx && !iface.objx.t == "i")
            throw new objx.exceptions.ArgumentException("Only one object and interfaces can be passed to the actsAs() method.");
        
        for (var property in iface.def)
        {
            var o = iface.def[property];
            
            if (o.objx)
            {
            	switch(o.objx.t)
            	{
            		case "m":

		                // test for this method
		                if (!objx.isf(test[property]))
		                {
		                    if (throwOnFailure)
		                        throw new objx.exceptions.DefinitionException("Method '" + property + "' is expected because of Interfaces or Abstract Methods in the abstract base class.");
		                    else
		                        return false;
		                }
		                else
		                {
		                    
		                    if (o.argc != null)
		                    {
		
		                        // check the argument count
		                        var argc = objx.argc(test[property]);
		                        
		                        if (argc != o.argc)
		                            if (throwOnFailure)
		                                throw new objx.exceptions.DefinitionException("Method '" + property + "' is expected to have " + o.argc + " argument(s) but has " + argc + " argument(s) because of Interfaces or Abstract Methods in the abstract base class.");
		                            else
		                                return false;
		
		                    }
		                    
		                    if (o.signature)
		                    {
		                    
		                        // make sure the argument names match
		                        var methodArgs = objx.args(test[property]);
		                        
		                        var signatureMatch = true;
		                        var expectedSignature = property + "(";
		                        var sigProbs = "";
		                        
		                        for (var i = 0, l = o.signature.length; i < l; i++)
		                        {
		                            if (methodArgs[i] != o.signature[i])
		                            {
		                                signatureMatch = false;
		                                sigProbs += "Argument " + (i + 1) + " should be \"" + o.signature[i] + "\" and not \"" + methodArgs[i] + "\". ";
		                                expectedSignature += o.signature[i];
		                                if (i < o.signature.length - 1)
		                                    expectedSignature += ", "
		                            }
		                        }
		                        
		                        if (!signatureMatch)
		                        {
		                            expectedSignature += ")";
		                            
		                            if (throwOnFailure)
		                                throw new objx.exceptions.DefinitionException("Method '" + property + "' expected to have signature " + expectedSignature + ". " + sigProbs);
		                            else
		                                return false;
		                            
		                        }
		                    
		                    }
		                    
		                }

            			break;
            		case "e":
            			
            			// make sure this event is defined
            			
            			if 	(
	            				!test[property]
	            				|| (typeof test[property] == "function" && (!test["notify_" + property] || !test["observe_" + property]))
	            				|| (typeof test[property] == "object" && (!test[property].objx || !test[property].objx.t || test[property].objx.t != "e"))	
            				)
            			{
            			
		                    if (throwOnFailure)
		                        throw new objx.exceptions.DefinitionException("Event '" + property + "' is expected because of Interfaces or Abstract Event in the abstract base class.");
		                    else
		                        return false;
		                        
            			}

            			break;
            		case "p":
            		
            			// make sure this property is defined
            			// (or at least something that looks like this property)

						if (test[property] != o)
							throw new objx.exceptions.DefinitionException(o.propertyType() + " property '" + property + "' is expected because of Interfaces or Abstract Event in the abstract base class.  Try adding: { " + property + ": " + o.propertyType() + " }");

            			break;
            	}
            }

        }
    }
    
    return true;

};

/*
 *  Properties
 */
objx.Property = objx.Class({
	
	extend: null,
	
	init: function(){
		
		// mark this item as an event description
        objx.st("t", this, "p");
	
	},
	
	addGetter: function(obj, name, isPrivate){
		var functionName = "get" + objx.pascal(name);
		if (isPrivate) functionName = "_" + functionName;
		obj[functionName] = function(){ return this[name]; };
	},
	addSetter: function(obj, name, isPrivate){
		var functionName = "set" + objx.pascal(name);
		if (isPrivate) functionName = "_" + functionName;
		obj[functionName] = function(v){ this[name] = v; return this; };
	}
	
});
 
objx.publicProperty 
	= new (
		objx.Class(objx.Property, 
			{ 
				extend: function(obj, name){
					this.addGetter(obj, name);
					this.addSetter(obj, name);
				},
				propertyType: function(){ return "Public"; }
			}
		)
	)
(); // create an instant instance

objx.privateProperty 
	= new (
		objx.Class(objx.Property, 
			{ 
				extend: function(obj, name){
					this.addGetter(obj, name, true);
					this.addSetter(obj, name, true);
				},
				propertyType: function(){ return "Private"; }
			}
		)
	)
(); // create an instant instance

objx.readOnlyProperty 
	= new (
		objx.Class(objx.Property, 
			{ 
				extend: function(obj, name){
					this.addGetter(obj, name);
					this.addSetter(obj, name, true);
				},
				propertyType: function(){ return "ReadOnly"; }
			}
		)
	)
(); // create an instant instance

objx.writeOnlyProperty 
	= new (
		objx.Class(objx.Property, 
			{ 
				extend: function(obj, name){
					this.addGetter(obj, name, true);
					this.addSetter(obj, name);
				},
				propertyType: function(){ return "WriteOnly"; }
			}
		)
	)
(); // create an instant instance



/*
 *  Exceptions
 */
objx.ExceptionBase = objx.Class(objx.abstract({

    message: null,

    init: function(comment, message){
    
        this.message = message;
        this.comment = comment;
        this.stackTrace = this.getStackTrace();
    
    },
    
    toString: function(){
        return this.getString();
    },
    
    getString: function(){
        return "[Exception: '" + this.message + ". " + this.comment + "']";
    },

    // taken from jsunit - thanks guys
    getStackTrace: function() {
        var result = '';

        if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA
            for (var a = arguments.caller; a != null; a = a.caller) {
                result += '> ' + a.callee + '\n';
                if (a.caller == a) {
                    result += '*';
                    break;
                }
            }
        }
        else { // Mozilla, not ECMA
            // fake an exception so we can get Mozilla's error stack
            var testExcp;
            try
            {
                foo.bar;
            }
            catch(testExcp)
            {
                var stack = this.parseErrorStack(testExcp);
                for (var i = 1; i < stack.length; i++)
                {
                    result += '> ' + stack[i] + '\n';
                }
            }
        }

        return result;
    },
    
    // taken from jsunit - thanks guys
    parseErrorStack: function(excp)
    {
        var stack = [];
        var name;

        if (!excp || !excp.stack)
        {
            return stack;
        }

        var stacklist = excp.stack.split('\n');

        for (var i = 0; i < stacklist.length - 1; i++)
        {
            var framedata = stacklist[i];

            name = framedata.match(/^(\w*)/)[1];
            if (!name) {
                name = 'anonymous';
            }

            stack[stack.length] = name;
        }
        // remove top level anonymous functions to match IE

        while (stack.length && stack[stack.length - 1] == 'anonymous')
        {
            stack.length = stack.length - 1;
        }
        return stack;
    }

}));

// the exception class
objx.exceptionClass = function(message){
    var exKlass = objx.Class(objx.ExceptionBase, {
        init: function(comment){
            this.base_init(comment, message);
        },
        toString: function(){
            return this.getString();
        }
    });
    exKlass.message = message;
    return exKlass;
};

// create the exceptions for objx
objx.exceptions = {
    ArgumentException: objx.exceptionClass("Incorrect or unexpected arguments encountered"),
    ClassException: objx.exceptionClass("Can not create Class"),
    DefinitionException: objx.exceptionClass("Definition is incomplete")
};

/*
 *  objx Method shortcuts
 */
objx.a = objx.abstract;
objx.aa = objx.actsAs;
objx.c = objx.Class;
objx.i = objx.interface;
objx.im = objx.implements;
objx.m = objx.method;
objx.e = objx.event;

// add shortcuts to the class methods
objx.classMethods.aa = objx.classMethods.actsAs;
objx.classMethods.im = objx.classMethods.implements;

/*
 *  Global namespaced shortcuts for nice clean
 *  client-side code
 */
var Abstract = objx.abstract;
var Class = objx.Class;
var Interface = objx.interface;
var Method = objx.method;
var Event = objx.event;
var AbstractClass = function(){
    arguments[arguments.length - 1] = objx.a(arguments[arguments.length - 1]);
    return objx.c.apply(this, arguments);
};
var Public = objx.publicProperty;
var Private = objx.privateProperty;
var ReadOnly = objx.readOnlyProperty;
var WriteOnly = objx.writeOnlyProperty;
